﻿/* ----------------------------------------------------------------------------
 * Copyright (c) Philip Stears 
 *
 * This source code is subject to terms and conditions of the Microsoft Public 
 * License (Ms-PL).
 * 
 * By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 * 
 * To request a copy of the Microsoft Public License, send a request by email
 * to license@connectbasic.net.
 *
 * This notice must not be removed from this software.
 * ---------------------------------------------------------------------------- */

namespace ConnectBasic.Shell.Implementation
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using ConnectBasic.Shell.Extensibility;

    internal class DefaultToolWindowZoneCollection : IToolWindowZoneCollection
    {
        private List<IToolWindowZone> mZones = new List<IToolWindowZone>();
        private Dictionary<string, IToolWindowZone> mZonesByName = new Dictionary<string, IToolWindowZone>(StringComparer.OrdinalIgnoreCase);
        private Dictionary<string, IList<IToolWindow>> mZoneAndStateToWindows = new Dictionary<string, IList<IToolWindow>>(StringComparer.OrdinalIgnoreCase);
        private Dictionary<string, IToolWindowZone> mWindowAndStateToZone = new Dictionary<string, IToolWindowZone>();

        #region Internal Methods

        internal IToolWindowZone Add(string invariantName)
        {
            IToolWindowZone zone = new DefaultToolWindowZone(invariantName);
            mZonesByName.Add(invariantName, zone);
            mZones.Add(zone);
            return zone;
        }

        #endregion

        #region IToolWindowZoneCollection Members

        IToolWindowZone IToolWindowZoneCollection.this[int index]
        {
            get { return mZones[index]; }
        }

        IToolWindowZone IToolWindowZoneCollection.this[string invariantName]
        {
            get { return mZonesByName.ItemOrDefault(invariantName); }
        }

        int IToolWindowZoneCollection.Count
        {
            get { return mZones.Count; }
        }

        void IToolWindowZoneCollection.AttachWindow(IToolWindowZone zone, IShellState state, IToolWindow window)
        {

            // Is it already registered for some other zone? 
            var existingZone = GetZone(state, window);

            if (existingZone != null)
            {
                DetachWindowCore(existingZone, state, window);
            }
            
            // Get or create the window list we're going to 
            // add to
            var windowList = GetWindowList(zone, state, true);

            // See if it's already attached in this state
            if (windowList.Contains(window))
            {
                return;
            }

            // Add it
            windowList.Add(window);
            SetZone(state, window, zone);
        }

        IEnumerable<IToolWindow> IToolWindowZoneCollection.GetAttachedWindows(IToolWindowZone zone, IShellState state)
        {
            // Get or create the window list we're going to 
            // add to
            var windowList = GetWindowList(zone, state, false);

            if (windowList == null)
            {
                return EmptyArray<IToolWindow>.Instance;
            }
            else
            {
                return new ReadOnlyCollection<IToolWindow>(windowList);
            }
        }

        #endregion

        #region IEnumerable<IToolWindowZone> Members

        IEnumerator<IToolWindowZone> IEnumerable<IToolWindowZone>.GetEnumerator()
        {
            return mZones.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return mZones.GetEnumerator();
        }

        #endregion

        #region Helpers

        private void DetachWindowCore(IToolWindowZone zone, IShellState state, IToolWindow window)
        {

            // Clear the zone
            SetZone(state, window, null);

            // Remove the window from the zone+state's window list
            var windowList = GetWindowList(zone, state, false);
            windowList.Remove(window);            
        }

        private IToolWindowZone GetZone(IShellState state, IToolWindow window)
        {
            string key = MakeStateAndWindowKey(state, window);
            return mWindowAndStateToZone.ItemOrDefault(key);
        }

        private void SetZone(IShellState state, IToolWindow window, IToolWindowZone zone)
        {
            string key = MakeStateAndWindowKey(state, window);
            mWindowAndStateToZone[key] = zone;
        }

        private static string MakeStateAndWindowKey(IShellState state, IToolWindow window)
        {
            return string.Format("{0}\n{1}", state.InvariantName, window.InvariantName);
        }

        private IList<IToolWindow> GetWindowList(IToolWindowZone zone, IShellState state, bool create)
        {
            string key = MakeZoneAndStateKey(zone, state);
            IList<IToolWindow> result;

            if (!mZoneAndStateToWindows.TryGetValue(key, out result) && create)
            {
                result = new List<IToolWindow>();
                mZoneAndStateToWindows.Add(key, result);
            }

            return result;
        }

        private static string MakeZoneAndStateKey(IToolWindowZone zone, IShellState state)
        {
            return string.Format("{0}\n{1}", zone.InvariantName, state.InvariantName);
        }

        #endregion

    }
}
